---@module mqttTask
--- 模块功能：MQTT客户端处理框架,华为云用
-- @author openLuat
-- @module mqtt.mqttTask
-- @license MIT
-- @copyright openLuat
-- @release 2018.03.28


require "misc"
require "mqtt"
require "mqttOutMsg"
require "mqttInMsg"
require "config"
require "deviceConfig"
module(..., package.seeall)
_G.MQTT_CLIENTID = deviceConfig.getData().mqtt.clientid
_G.MQTT_USERNAME = deviceConfig.getData().mqtt.username
_G.MQTT_PASSWORD = deviceConfig.getData().mqtt.password

-- 设备消息上报
_G.MQTT_TOPIC_MESSAGEUP = "$oc/devices/"..MQTT_USERNAME.."/sys/messages/up"

-- 属性上报
_G.MQTT_TOPIC_PROREPORT = "$oc/devices/"..MQTT_USERNAME.."/sys/properties/report"
-- 平台设置属性值$oc/devices/{device_id}/sys/properties/set/request_id={request_id}
_G.MQTT_TOPIC_PROSET_REQ = "$oc/devices/"..MQTT_USERNAME.."/sys/properties/set/"
-- 平台查询设备属性 $oc/devices/{device_id}/sys/properties/get/request_id={request_id}
_G.MQTT_TOPIC_PROSGET = "$oc/devices/"..MQTT_USERNAME.."/sys/properties/get/"

-- 平台下发命令给设备 $oc/devices/{device_id}/sys/commands/request_id={request_id},华为好像不支持+通配符用#
_G.MQTT_TOPIC_COMMANDS_REQ = "$oc/devices/"..MQTT_USERNAME.."/sys/commands/"
-- 设备返回命令响应 $oc/devices/{device_id}/sys/commands/response/request_id={request_id}


local ready = false

--- MQTT连接是否处于激活状态
-- @return 激活状态返回true，非激活状态返回false
-- @usage mqttTask.isReady()
function isReady()
    return ready
end

-- 更新mqtt错误重启的判断次数
-- 一开始是要等待10分钟(26)，然后是60分钟(156)，然后是12小时(1872)，然后就是每天(3744)重启一次
local function updateErrCounts()
    local counts = nvm.get("MQTT_ERR_RST_TIME")
    if counts < 30 then
        nvm.set("MQTT_ERR_RST_TIME", 156)
    elseif counts < 160 then
        nvm.set("MQTT_ERR_RST_TIME", 1872)
    elseif counts < 1900 then
        nvm.set("MQTT_ERR_RST_TIME", 3744)
    elseif counts < 3800 then
        nvm.set("MQTT_ERR_RST_TIME", 3744)
    end
end

function startMqtt()
    --启动MQTT客户端任务
    sys.taskInit(function()
        local retryConnectCnt = 0
        local errorCnt = 0
        sys.wait(10000)
        while true do
            if socket.isReady() == false then
                retryConnectCnt = 0
                --等待网络环境准备就绪，超时时间是5分钟
                sys.waitUntil("IP_READY_IND", 300000)
            end

            if socket.isReady() == true then
                log.info("mqtt.task", "mqtt task is startting...")
                --创建一个MQTT客户端,经过试验30秒心跳满足集团的网络需求不会一直断线在线
                local mqttClient = mqtt.client(_G.MQTT_CLIENTID, 30, _G.MQTT_USERNAME, _G.MQTT_PASSWORD)
                --阻塞执行MQTT CONNECT动作，直至成功
                --如果使用ssl连接，打开mqttClient:connect("lbsmqtt.airm2m.com",1884,"tcp_ssl",{caCert="ca.crt"})，根据自己的需求配置
                --mqttClient:connect("lbsmqtt.airm2m.com",1884,"tcp_ssl",{caCert="ca.crt"})
                log.info("mqtt.task", "mqtt conectting..., host:" .. config.MQTT_HOST  .. ",clientId:" .. _G.MQTT_CLIENTID)
                local mqttre = false
                if config.IS_MQTT_SSL == true then
                    -- tcp_ssl连接
                    mqttre = mqttClient:connect(config.MQTT_HOST, config.MQTTS_PORT,"tcp_ssl",nil)
                    log.info("mqtt.task ","use ssl,port:" .. config.MQTTS_PORT)
                else
                    mqttre = mqttClient:connect(config.MQTT_HOST, config.MQTT_PORT,"tcp",nil)
                    log.info("mqtt.task ",",port:" .. config.MQTT_PORT)
                end
                if mqttre then
                    -- if mqttClient:connect("lbsmqtt.airm2m.com",1884,"tcp") then
                    log.info("mqtt.task", "mqtt link success subscribe topic :", _G.MQTT_TOPIC_COMMANDS_REQ..",".._G.MQTT_TOPIC_PROSET_REQ)
                    retryConnectCnt = 0
                    ready = true
                    sys.publish("MQTT_READY", true)
                    --订阅主题(api 调用监听) ,命令接收topic必须等级2，否则收到suback grant qos error
                    if mqttClient:subscribe({ [_G.MQTT_TOPIC_COMMANDS_REQ .. '#'] = 1,
                                              [_G.MQTT_TOPIC_PROSGET .. '#'] = 1,
                                              [_G.MQTT_TOPIC_PROSET_REQ .. '#'] = 1 }) then
                        mqttOutMsg.init()
                        --循环处理接收和发送的数据
                        while true do
                            if not mqttInMsg.proc(mqttClient) then
                                log.error("mqttTask.mqttInMsg.proc error")
                                break
                            end
                            log.info("mqttInMsg.proc 2 mqttOutMsg.proc")
                            if not mqttOutMsg.proc(mqttClient) then
                                log.error("mqttTask.mqttOutMsg proc error")
                                break
                            end
                        end
                        mqttOutMsg.unInit()
                    end
                    ready = false
                    sys.publish("MQTT_READY", false)
                else
                    retryConnectCnt = retryConnectCnt + 1
                    errorCnt = errorCnt + 1
                    log.info("mqtt.task","retryConnectCnt = "..retryConnectCnt.." errorCnt = "..errorCnt)
                end
                if _G.ISW5500 == false then
                    --断开MQTT连接
                    mqttClient:disconnect()
                end
                if retryConnectCnt >= 5 then
                    if _G.ISW5500 == true then
                        w5500.init()
                    else
                        link.shut()
                    end
                    retryConnectCnt = 0
                    --测试验证，如果mqtt无故离线，尝试post日志到服务器，带上当前一些网络参数，看是否可以成功 2021/4/21
                    logfile.addExtraLog("MQTT_ERR",net.getNetMode(),net.getState(),net.getRssi())
                end
                sys.wait(5000)
            else
                --进入飞行模式，20秒之后，退出飞行模式
                net.switchFly(true)
                sys.wait(10000)
                net.switchFly(false)
            end

            --重启
            if errorCnt > nvm.get("MQTT_ERR_RST_TIME") then
                updateErrCounts()
                sys.restart("NET_ERROR_REBOOT:" .. tostring(nvm.get("MQTT_ERR_RST_TIME")))
            end
        end
    end
    )
end


